package hms.ReservationsModule;

import hms.database.DAO;
import hms.database.DAOnoGUI;
import hms.database.DBJFrame;
import hms.generic.AbstractGlobalView;
import hms.payment.*;
import hms.reservations.*;
import hms.rooms.Room;
import hms.rooms.RoomTable;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * This class sets up Booking and Check-In Sub-Module (BCSM).  Includes functions for the following:
 * -reserving room
 * -booking and checking in guest
 * -selecting payment methods 
 * 
 * @author Ben Adcock
 */

public class CheckInView extends AbstractGlobalView implements ListSelectionListener{
	
	//defines global components
	private JLabel guestName, reservationStatus, balanceLabel;
	private JButton chooseReservation, chooseRoom, checkGuestIn, choosePaymentOption;
	private RoomsLabel roomsLabel;
	
	private DBJFrame<GuestTable, Guest> guestJFrame;
	
	private RoomTable selectedRooms;
	private RoomTable allRooms;
	private PaymentProcessor payments;
	private int numberOfNights;
	
	private Guest selectedGuest;
	private Reservation selectedReservation;
	
	public CheckInView(){	
		super.update(createCheckInView());		
		super.display();
	}
	
	//initializes all components for Booking/Check-In sub-module
	// will work with overridden method from RSVM class
	public JPanel createCheckInView(){
		
		/////////////////////////////
		//// Create GUI elements ////
		/////////////////////////////

		JPanel checkInModulePanel = new JPanel();
		JPanel guestSelection = new JPanel();
		guestSelection.setBorder(BorderFactory.createTitledBorder("Select Guest"));
		JPanel checkInStatus = new JPanel();
		checkInStatus.setBorder(BorderFactory.createTitledBorder("Check-In Status"));
		JPanel buttonPanel = new JPanel();
		guestJFrame = new DBJFrame<GuestTable, Guest>(new GuestTable());
		
		guestName = new JLabel("Guest Name: ");
		reservationStatus = new JLabel("Reservation Status: ");
		balanceLabel = new JLabel("Current Balance: $");
		roomsLabel = new RoomsLabel();
		chooseReservation = new JButton("Select Guest");
		chooseRoom = new JButton("Choose Room");
		choosePaymentOption = new JButton("Receive Payment");
		checkGuestIn = new JButton("Check Into Hotel");
		
		//////////////////////////////
		//// Listener assignments ////
		//////////////////////////////
		
		chooseReservation.setActionCommand("chooseReservation");
		chooseReservation.addActionListener(this);
		chooseRoom.setActionCommand("reserveRoom");
		chooseRoom.addActionListener(this);
		checkGuestIn.setActionCommand("checkGuestIn");
		checkGuestIn.addActionListener(this);
		choosePaymentOption.setActionCommand("paymentOption");
		choosePaymentOption.addActionListener(this);
		guestJFrame.getJList().addListSelectionListener( this );
		
		//guest.addListSelectionListener(this);
		
		/////////////////////////
		//// Layout elements ////
		/////////////////////////
		
		GridBagConstraints c;
		
		// Select guest panel
		c = new GridBagConstraints();
		guestSelection.setLayout(new GridBagLayout());
		c.insets = new Insets(10,10,10,10);
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		
		// Pane - for guest selection panel
		c.gridx = 0; c.gridy = 0; c.gridwidth = 2; c.weighty = 1; 
		guestSelection.add(guestJFrame.getContentPane(),c);
		
		// Buttons - for buttonPanel
		c.gridx = 1; c.gridy = 1; c.gridwidth = 1; c.weighty = 0;
		buttonPanel.add(chooseReservation);
		c.gridx = 2;
		buttonPanel.add(chooseRoom, c);
		c.gridx = 4;
		buttonPanel.add(choosePaymentOption, c);
		c.gridx = 5;
		buttonPanel.add(checkGuestIn,c);
		
		chooseReservation.setEnabled(false);
		checkGuestIn.setEnabled(false);
		chooseRoom.setEnabled(false);
		choosePaymentOption.setEnabled(false);
		
		// Check-In Status Panel
		c = new GridBagConstraints();
		checkInStatus.setLayout(new GridBagLayout());
		c.weightx = .2;
		
		// Labels - Check-In Status Panel components
		c.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING;
		c.weighty = .5;
		
		//first column
		c.gridx = 0; c.gridy = 0;
		checkInStatus.add(guestName, c);
		
		c.gridy = 1;
		checkInStatus.add(reservationStatus, c);
		
		c.gridy = 2;
		checkInStatus.add(roomsLabel, c);
		
		c.gridy = 3;
		//checkInStatus.add(checkInDate, c);
		
		c.gridy = 4;
		//checkInStatus.add(checkOutDate, c);
		
		c.gridy = 5;
		checkInStatus.add(balanceLabel, c);
				
		// Main Panel
		c = new GridBagConstraints();
		checkInModulePanel.setLayout(new GridBagLayout());
		c.insets = new Insets(20,10,10,10);
		c.anchor = GridBagConstraints.CENTER;
		c.fill = GridBagConstraints.BOTH;
		
		c.gridx = 0; c.gridy = 0; c.weightx = 0;
		checkInModulePanel.add(guestSelection,c);
		c.gridx = 1; c.weightx = 1;
		checkInModulePanel.add(checkInStatus,c);
		c.gridy = 2;
		checkInModulePanel.add(buttonPanel, c);
		
		return checkInModulePanel;
	}
	
	private class RoomsLabel extends JLabel {

		private static final long serialVersionUID = 617335382344263176L;
		
		boolean firstRoom;
		
		public RoomsLabel() {
			super();
			reset();
		}
		
		public void reset() {
			firstRoom = true;
			setText("Rooms Selected: None");
		}
		
		private void addRoom(Room r) {
			if(firstRoom) {
				setText("Rooms selected: " + r.getRoomLabel());
			firstRoom = false;
			} else {
				setText(getText() + ", " + r.getRoomLabel());
			}
		}
		
		public void update() {
			reset();
			for(Room r : selectedRooms) {
				addRoom(r);
			}
		}
		
		public void loadByReservationID(int reservationID) {
			reset();
			DAO localDAO = DAOnoGUI.getDAO();
			
				ResultSet rs = localDAO.getResult("SELECT ROOMID FROM ROOM_RESERVATION WHERE RESERVATION_ID="+reservationID+";");
				try {
					while(rs.next()) {
						addRoom(new Room(rs.getInt("ROOMID")));
					}
				} catch (SQLException e) {
				}
			
			
			DAOnoGUI.closeDAO();
		}
		
	}
	
	/**
	 * Select guest to check-in
	 * 
	 * Here goes check-in init code
	 */
	private void initCheckInProcess() {
		// Load guest
		selectedGuest = guestJFrame.getSelectedRowObject();
		// initialize session variables
		numberOfNights = 1;
		allRooms = getFilteredRoomTable();
		selectedRooms = selectedReservation.loadAssociatedRooms();
		payments = new PaymentProcessor(selectedGuest.getUniqueIdentifier(),calculateBalance());
		// disable list
		guestJFrame.getJList().setEnabled(false);
		// update display
		chooseReservation.setActionCommand("Cancel");
		chooseReservation.setText("Cancel");
		chooseRoom.setEnabled(true);
		choosePaymentOption.setEnabled(true);
		updateBalanceDisplay();
		roomsLabel.update();
		reservationStatus.setText("Reservation status: Checking in");
		
	}

	private RoomTable getFilteredRoomTable() {
		Calendar c = new GregorianCalendar();
		long twentyfour = 24 * 60 * 60 * 1000;
		RoomTable result = new RoomTable();
		result.filterAvailableRoomsDuring(new Date(c.getTimeInMillis()), new Date(c.getTimeInMillis()+twentyfour));
		Iterator<Room> i = result.iterator();
		while(i.hasNext()) {
			Room r = i.next();
			if(r.getOccupationStatus().equalsIgnoreCase("Out of Commission")) {
				i.remove();
			}
		}
		return result;
	}

	
	/**
	 * Display room-choosing pop-up, and update display as necessary. 
	 */
	private void selectAndAddRoom() {
		DBJFrame<RoomTable, Room> frame = new DBJFrame<RoomTable,Room>(allRooms);
		frame.showDialog("Select a room");
		Room result = frame.getSelectedRowObject();
		if(result != null) {
			allRooms.remove(result);
			selectedRooms.add(result);
			roomsLabel.update();
			updateBalanceDisplay();
			informationWillBeLost();
		}
	}
	
	private void selectPaymentType() {
		payments.newPaymentPopup();
		updateBalanceDisplay();
		informationWillBeLost();
	}
	
	// TODO write check in code
	
	private void checkInGuest() {
		// Fill out reservation
		selectedReservation.setBookedInHotel(true);
		selectedReservation.setCheckInDate(readCheckInDate());
		selectedReservation.setCheckOutDate(readCheckOutDate());
		selectedReservation.setIsCheckedIn(true);
		selectedReservation.setIsChild(false);
		selectedReservation.setPayingCustomerID(selectedGuest.getUniqueIdentifier());
		selectedReservation.setReservationFee(0);
		selectedReservation.setRewardsPointsEarned(calculateRewards());
		
		// Commit reservation to database
		selectedReservation.commitToDatabase();
		
		// Associate rooms to reservation
		DAO localDAO = DAOnoGUI.getDAO();
		for(Room r : selectedRooms) {
			selectedGuest.setRoomID(r.getUniqueIdentifier());
			r.setOccupationStatus("Occupied");
			r.setReservationID(selectedReservation.getUniqueIdentifier());
			localDAO.updateCommand("INSERT INTO ROOM_RESERVATION (ROOMID, RESERVATION_ID) VALUES (" + r.getUniqueIdentifier() + ", " + selectedReservation.getUniqueIdentifier() + ");");
			r.commitToDatabase();
		}
		// Update guest information
		selectedGuest.commitToDatabase();
		// Associate Guest with reservation
		localDAO.updateCommand("INSERT INTO GUEST_RESERVATION (RESERVATION_ID, GUEST_ID) VALUES ("+selectedReservation.getUniqueIdentifier()+", "+selectedGuest.getUniqueIdentifier()+");");
		
		
		// Save payment information
		payments.commitToDatabase();
		informationHasBeenSaved();
		DAOnoGUI.closeDAO();
		
		JOptionPane.showMessageDialog(null, selectedGuest.getFirstName() + " has been checked in.");
		
		// Go back to initial state
		resetGUI();
	}
	
	private void resetGUI() {
		roomsLabel.reset();
		selectedGuest = null;
		guestJFrame.getJList().setEnabled(true);
		// TODO get deselect code
		guestJFrame.getJList().setSelectedIndex(0);
		choosePaymentOption.setEnabled(false);
		chooseRoom.setEnabled(false);
		checkGuestIn.setEnabled(false);
		chooseReservation.setActionCommand("chooseReservation");
		chooseReservation.setText("Select Guest");
		putGuestDataInGUI();
	}
	
	
	
	private int calculateRewards() {
		return 0;
	}

	private Date readCheckOutDate() {
		// Return tomorrow
		Calendar c = new GregorianCalendar();
		final long twentyFourHoursInMillis = 24*60*60*1000;
		return new Date(c.getTimeInMillis()+twentyFourHoursInMillis);
	}

	private Date readCheckInDate() {
		// Return today
		Calendar c = new GregorianCalendar();
		return new Date(c.getTimeInMillis());
	}

	// Display current balance after change to balance
	private void updateBalanceDisplay() {
		double balance = calculateBalance();
		balanceLabel.setText(String.format("Current Balance: $%.2f", balance));
		// check that guest has at least 1 room and $0 balance
		// to allow check-in
		if(Math.abs(balance) < 0.004 && selectedRooms.size() > 0) { // floating point error-tolerant check for 0
			checkGuestIn.setEnabled(true);
			choosePaymentOption.setEnabled(false);
		} else {
			checkGuestIn.setEnabled(false);
			choosePaymentOption.setEnabled(true);
		}
	}

	// Calculate current balance
	private double calculateBalance() {
		double balance = 0;
		// Add to balance all room charges
		DAO localDAO = DAOnoGUI.getDAO();
		for(Room r : selectedRooms) {
			ResultSet rs = localDAO.getResult("SELECT PRICEPERNIGHT FROM ROOM_TYPE WHERE ROOMTYPELABEL='"+r.getRoomTypeLabel()+"';");
			try {
				if(rs.next()) {
				balance += rs.getInt("PRICEPERNIGHT")*numberOfNights;
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// Update balance for payments, and deduct what has been paid
		if(payments!=null) {
			payments.setRemainingBalance(balance);
		balance -= payments.retrievePendingCredits();
		}
		DAOnoGUI.closeDAO();
		return balance;
	}

	/**
	 * On menu change, check database for existing reservation, and update GUI
	 * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
	 */
	public void valueChanged(ListSelectionEvent event) {
		if (!event.getValueIsAdjusting()) {
			// Check for valid value
			selectedGuest = guestJFrame.getSelectedRowObject();
			putGuestDataInGUI();		
		}
	}

	private void putGuestDataInGUI() {
		// Display guest and booking status
		if (selectedGuest == null) {
			// Clear fields
			guestName.setText("" + "Guest Name: ");
			// Disable reservation button
			chooseReservation.setEnabled(false);
		} else {
			// Check database for reservation
			ReservationView rv = new ReservationView();
			rv.fillForGuest(selectedGuest);
			selectedReservation = rv.nearestToToday();
			if(selectedReservation == null) { selectedReservation = new Reservation(); }
			
			guestName.setText("" + "Guest Name: "
					+ selectedGuest.toString());
			if(!selectedReservation.getBookedInHotel()) {
				reservationStatus.setText("Reservation Status: Not made");
				chooseReservation.setEnabled(true);
				balanceLabel.setText("Current Balance: $0.00");
				roomsLabel.reset();
			} else if (!selectedReservation.getIsCheckedIn()) {
				reservationStatus.setText("Reservation Status: Booked");
				balanceLabel.setText(String.format("Current Balance: $%.2f",selectedReservation.getReservationFee()));
				roomsLabel.loadByReservationID(selectedReservation.getUniqueIdentifier());
				chooseReservation.setEnabled(true);
			} else {
				reservationStatus.setText("Reservation Status: Checked in");
				balanceLabel.setText("Current Balance: $-.--");
				roomsLabel.loadByReservationID(selectedReservation.getUniqueIdentifier());
				chooseReservation.setEnabled(false);
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand() == "chooseReservation") {
			initCheckInProcess();

		} else if (e.getActionCommand() == "reserveRoom") {
			selectAndAddRoom();
		}

		else if (e.getActionCommand() == "Cancel") {
			int response = JOptionPane.showOptionDialog(null, "Changes will be disarded! OK?", "Discard changes", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null);
			if(response==JOptionPane.OK_OPTION) {
				informationHasBeenSaved();
				resetGUI();
			}
		}

		else if (e.getActionCommand() == "checkGuestIn") {
			checkInGuest();

		}

		else if (e.getActionCommand() == "paymentOption") {
			selectPaymentType();
		}
	}
}
